package base

import (
	"fmt"
	"os"
	"runtime"
	"strconv"

	"github.com/labstack/echo"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var Logger *zap.Logger

func SyncLogger() {
	Logger.Sync()
}

func InitLogger() {
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	Logger = zap.New(core, zap.AddCaller())
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	return zapcore.NewConsoleEncoder(encoderConfig)
}

func getLogWriter() zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   AppConf.Zap.Filename,
		MaxSize:    AppConf.Zap.MaxSize,
		MaxBackups: AppConf.Zap.MaxBackups,
		MaxAge:     AppConf.Zap.MaxAges,
		Compress:   AppConf.Zap.Compress,
	}
	if AppConf.Zap.Filename == "" {
		return zapcore.AddSync(os.Stdout)
	} else if AppConf.Zap.Console {
		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger))
	}
	return zapcore.AddSync(lumberJackLogger)
}

func logMiddlewareHandler(c echo.Context, next echo.HandlerFunc) error {
	req := c.Request()
	res := c.Response()
	var err error
	if err = next(c); err != nil {
		c.Error(err)
	}
	p := req.URL.Path
	if p == "" {
		p = "/"
	}
	bytesIn := req.Header.Get(echo.HeaderContentLength)
	if bytesIn == "" {
		bytesIn = "0"
	}
	if err != nil {
		Logger.Error(err.Error(), zap.String("remote_ip", c.RealIP()), zap.String("host", req.Host),
			zap.String("path", p), zap.String("method", req.Method), zap.String("status", strconv.Itoa(res.Status)))
	} else {
		Logger.Info("", zap.String("remote_ip", c.RealIP()), zap.String("host", req.Host),
			zap.String("path", p), zap.String("method", req.Method), zap.String("status", strconv.Itoa(res.Status)))
	}
	return nil
}

func loggerFunc(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		return logMiddlewareHandler(c, next)
	}
}

//Hook 返回 echo中间件
func Hook() echo.MiddlewareFunc {
	return loggerFunc
}

func RecoverMiddleware() echo.MiddlewareFunc {
	return func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(ctx echo.Context) error {
			defer func() {
				if r := recover(); r != nil {
					err, ok := r.(error)
					if !ok {
						err = fmt.Errorf("%v", r)
					}
					//4KB
					stackSize := 4 << 10
					stack := make([]byte, stackSize)
					length := runtime.Stack(stack, true)
					Logger.Error("异常捕获中间件, 发生panic异常", zap.Error(err), zap.ByteString("stack", stack[:length]))

					//交给HTTPErrorHandler处理后续流程: 返回给用户{"message":"Internal Server Error"}
					ctx.Error(err)
				}
			}()
			//执行action业务逻辑
			return next(ctx)
		}
	}
}
